package pl.dsp.dsp1.processing;

public class SincInterpolator implements Generator {

	private Signal signal;
	private int significantSamples;

	public SincInterpolator(Signal signal, int significantSamples) {
		this.signal = signal;
		this.significantSamples = significantSamples;
	}

	private double sinc(double x) {
		return x == 0 ? 1.0 : (Math.sin(Math.PI * x) / (Math.PI * x));
	}

	@Override
	public SignalValue at(double x) {
		double relativePosition = (x - signal.getT1())
				/ signal.getSamplingPeriod();

		int previous = (int) Math.floor(relativePosition);
		int next = (int) Math.ceil(relativePosition);
		double progress = relativePosition - previous;

		if (progress < 0.0001)
			return (SignalValue) signal.get(previous).clone();
		if (progress > 0.9999)
			return (SignalValue) signal.get(next).clone();

		int closestSample = progress <= 0.5 ? previous : next;

		SignalValue sum = (SignalValue) signal.get(closestSample).multiply(
				new SimpleValue(sinc(relativePosition - closestSample)));
		int processed = 1;
		int radius = 2;
		while (processed < significantSamples && radius < 2*signal.size()) {
			int index = (radius % 2 == 0) ? closestSample - radius / 2
					: closestSample + radius / 2;
			radius++;

			if (index < 0 || index > signal.size() - 1)
				continue;

			sum = sum.add(signal.get(index).multiply(
					new SimpleValue(sinc(relativePosition - index))));
			processed++;
		}

		return sum;
	}

	@Override
	public boolean isPeriodical() {
		return false;
	}

}
